import React, { useRef, useEffect, useState, useCallback } from "react";
import { cn } from "./utils";

interface ScrollbarXYProps {
  children: React.ReactNode;
  width?: number | string;
  height?: number | string;
  className?: string;
  style?: React.CSSProperties;
}

const MIN_THUMB_SIZE = 30;
const BAR_SIZE = 10;

const ScrollbarXY: React.FC<ScrollbarXYProps> = ({
  children,
  width = 400,
  height = 300,
  className = "",
}) => {
  const containerRef = useRef<HTMLDivElement>(null);
  const contentRef = useRef<HTMLDivElement>(null);

  // Thumb size and position
  const [thumbYHeight, setThumbYHeight] = useState(MIN_THUMB_SIZE);
  const [thumbYTop, setThumbYTop] = useState(0);
  const [thumbXWidth, setThumbXWidth] = useState(MIN_THUMB_SIZE);
  const [thumbXLeft, setThumbXLeft] = useState(0);

  // Dragging state
  const [draggingY, setDraggingY] = useState(false);
  const [startY, setStartY] = useState(0);
  const [startYTop, setStartYTop] = useState(0);

  const [draggingX, setDraggingX] = useState(false);
  const [startX, setStartX] = useState(0);
  const [startXLeft, setStartXLeft] = useState(0);

  const [showBar, setShowBar] = useState(false);
  const [showX, setShowX] = useState(false);
  const [showY, setShowY] = useState(false);

  // Update thumb size and position
  const updateThumbs = () => {
    const container = containerRef.current;
    const content = contentRef.current;
    if (!container || !content) return;

    // Vertical
    const ratioY = container.clientHeight / content.scrollHeight;
    const heightY = Math.max(container.clientHeight * ratioY, MIN_THUMB_SIZE);
    setThumbYHeight(heightY);

    // 安全处理边界情况
    const verticalScrollRange = content.scrollHeight - container.clientHeight;
    if (verticalScrollRange <= 0) {
      setThumbYTop(0);
    } else {
      const scrollRatio = container.scrollTop / verticalScrollRange;
      setThumbYTop(scrollRatio * (container.clientHeight - heightY));
      setShowY(true);
    }

    // Horizontal
    const ratioX = container.clientWidth / content.scrollWidth;
    const widthX = Math.max(container.clientWidth * ratioX, MIN_THUMB_SIZE);
    setThumbXWidth(widthX);

    // 安全处理边界情况
    const horizontalScrollRange = content.scrollWidth - container.clientWidth;
    if (horizontalScrollRange <= 0) {
      setThumbXLeft(0);
    } else {
      const scrollRatio = container.scrollLeft / horizontalScrollRange;
      setThumbXLeft(scrollRatio * (container.clientWidth - widthX));
      setShowX(true);
    }
  };

  // 使用防抖函数减少不必要的更新
  const handleScroll = useCallback(() => {
    requestAnimationFrame(updateThumbs);
  }, []);

  useEffect(() => {
    updateThumbs();
    const container = containerRef.current;
    if (!container) return undefined;
    container.addEventListener("scroll", handleScroll);
    window.addEventListener("resize", handleScroll);

    return () => {
      container.removeEventListener("scroll", handleScroll);
      window.removeEventListener("resize", handleScroll);
    };
  }, [handleScroll]);

  // Drag Y
  const onMouseMoveY = useCallback(
    (e: MouseEvent) => {
      if (!draggingY || !containerRef.current || !contentRef.current) return;
      const container = containerRef.current;
      const content = contentRef.current;
      const delta = e.clientY - startY;
      const newTop = Math.min(
        Math.max(startYTop + delta, 0),
        container.clientHeight - thumbYHeight
      );
      setThumbYTop(newTop);
      const scrollRatio = newTop / (container.clientHeight - thumbYHeight || 1);
      container.scrollTop =
        scrollRatio * (content.scrollHeight - container.clientHeight);
    },
    [draggingY, startY, startYTop, thumbYHeight]
  );

  const onMouseUpY = useCallback(() => {
    setDraggingY(false);
  }, []);
  useEffect(() => {
    if (draggingY) {
      document.addEventListener("mousemove", onMouseMoveY);
      document.addEventListener("mouseup", onMouseUpY);
    }
    return () => {
      document.removeEventListener("mousemove", onMouseMoveY);
      document.removeEventListener("mouseup", onMouseUpY);
    };
  }, [draggingY, onMouseMoveY, onMouseUpY, startY, startYTop, thumbYHeight]);

  // Drag X
  const onMouseMoveX = useCallback(
    (e: MouseEvent) => {
      if (!draggingX || !containerRef.current || !contentRef.current) return;
      const container = containerRef.current;
      const content = contentRef.current;
      const delta = e.clientX - startX;
      const newLeft = Math.min(
        Math.max(startXLeft + delta, 0),
        container.clientWidth - thumbXWidth
      );
      setThumbXLeft(newLeft);
      const scrollRatio = newLeft / (container.clientWidth - thumbXWidth || 1);
      container.scrollLeft =
        scrollRatio * (content.scrollWidth - container.clientWidth);
    },
    [draggingX, startX, startXLeft, thumbXWidth]
  );
  const onMouseUpX = useCallback(() => {
    setDraggingX(false);
  }, []);
  useEffect(() => {
    if (draggingX) {
      document.addEventListener("mousemove", onMouseMoveX);
      document.addEventListener("mouseup", onMouseUpX);
    }
    return () => {
      document.removeEventListener("mousemove", onMouseMoveX);
      document.removeEventListener("mouseup", onMouseUpX);
    };
  }, [draggingX, onMouseMoveX, onMouseUpX, startX, startXLeft, thumbXWidth]);

  // Thumb mousedown
  const onThumbYMouseDown = (e: React.MouseEvent) => {
    e.preventDefault();
    setDraggingY(true);
    setStartY(e.clientY);
    setStartYTop(thumbYTop);
  };
  const onThumbXMouseDown = (e: React.MouseEvent) => {
    e.preventDefault();
    setDraggingX(true);
    setStartX(e.clientX);
    setStartXLeft(thumbXLeft);
  };

  return (
    <div
      className={cn(
        "relative bg-gray-100 rounded-lg shadow-inner overflow-hidden",
        className
      )}
      style={{ width, height }}
      onMouseOver={() => setShowBar(true)}
      onMouseOut={() => setShowBar(false)}
    >
      <div
        ref={containerRef}
        className="w-full h-full overflow-scroll scrollbar-hide" // hide native scrollbar
        style={{
          // 兼容一些浏览器隐藏滚动条
          msOverflowStyle: "none",
          scrollbarWidth: "none",
        }}
      >
        <div ref={contentRef}>{children}</div>
      </div>
      {/* Vertical track */}
      <div className="absolute top-0 right-0" style={{ width: BAR_SIZE }}>
        <div
          className="absolute right-0 rounded bg-gray-300 hover:bg-blue-400 cursor-pointer"
          style={{
            height: thumbYHeight,
            top: thumbYTop,
            opacity: showBar ? 0.7 : 0,
            width: BAR_SIZE,
          }}
          onMouseDown={onThumbYMouseDown}
        />
      </div>
      {/* Horizontal track */}
      <div className="absolute left-0 bottom-0" style={{ height: BAR_SIZE }}>
        <div
          className="absolute left-0 rounded bg-gray-300 hover:bg-blue-400 cursor-pointer"
          style={{
            width: thumbXWidth,
            left: thumbXLeft,
            opacity: showBar ? 0.7 : 0,
            height: BAR_SIZE,
          }}
          onMouseDown={onThumbXMouseDown}
        />
      </div>
    </div>
  );
};

export default ScrollbarXY;
